package cn.hill4j.tool.spring.ext.mock;

import org.aopalliance.aop.Advice;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.CollectionUtils;

import java.util.Set;
/**
  * ClassName: MockAdvisor
  * Description: mock切面增强器
  * @author hillchen
  * Date: 2023/8/12
  * @version: 1.0
  */
public class MockAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    /**
     * 增强器
     */
    private final Advice advice;
    /**
     * 切入点
     */
    private final Pointcut pointcut;

    /**
     * @author hillchen
     * Description: 构造方法
     * Date: 2023/8/12 23:44
     * @param advice 增强器
     * @param mockSourceClass 需要mock源类
     **/
    public MockAdvisor(Advice advice, Set<Class> mockSourceClass) {
        this.advice = advice;
        this.pointcut = buildPointcut(mockSourceClass);
    }

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
        return advice;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (this.advice instanceof BeanFactoryAware) {
            ((BeanFactoryAware)this.advice).setBeanFactory(beanFactory);
        }
    }

    private Pointcut buildPointcut(Set<Class> mockSourceClass) {
        return new ClassesPoint(mockSourceClass) ;
    }

    /**
      * ClassName: ClassesPoint
      * Description: class类型mock切入点
      * @author hillchen
      * Date: 2023/8/12
      * @version: 1.0
      */
    private static class ClassesPoint implements Pointcut {
        /**
         * 需要mock的bean类型集合
         */
        private Set<Class> classes;
        /**
         * @author hillchen
         * Description: 构造方法
         * Date: 2023/8/12 23:47
         * @param classes 需要mock的bean类型集合
         **/
        public ClassesPoint(Set<Class> classes) {
            this.classes = classes;
        }

        @Override
        public ClassFilter getClassFilter() {
            return new SetClassFilter(classes);
        }

        @Override
        public MethodMatcher getMethodMatcher() {
            return MethodMatcher.TRUE;
        }
    }

    /**
      * ClassName: MockAdvisor
      * Description: 类型集合过滤器
      * @author hillchen
      * Date: 2023/8/12
      * @version: 1.0
      */
    private static class SetClassFilter implements ClassFilter {
        /**
         * 能够进行匹配的class类集合
         */
        private Set<Class> classes;

        /**
         * @author hillchen
         * Description: 构造方法
         * Date: 2023/8/12 23:49
         * @param classes 能够进行匹配的class类集合
         **/
        public SetClassFilter(Set<Class> classes) {
            this.classes = classes;
        }
        /**
         * @author hillchen
         * Description: 类型过去匹配方法
         * Date: 2023/8/12 23:49
         * @param candidate 匹配类型
         * @return boolean 如果candidate在 classes集合中则返回true
         **/
        @Override
        public boolean matches(Class<?> candidate) {
            if (CollectionUtils.isEmpty(classes)){
                return false;
            }
            for (Class clazz: classes){
                if (clazz.isAssignableFrom(candidate)){
                    return true;
                }
            }
            return false;
        }
    }
}
